#include "BaseNode.hpp"

using namespace BFT;

BaseNode::BaseNode(BFT::util::Role _myrole, boost::shared_ptr<BFT::membership::Membership> _membership)
{
	membership = _membership;
	myid = membership.get()->getMyId();
	
	myRole = _myrole;
	//allocate the space for the vector of sockets used for outgoing messages
	//just make the space for now, connections will be opened only if needed
	to_client=(int*)malloc(sizeof(int)*BFT::Parameters::getNumberOfClients() );
	for(int i=0 ; i<BFT::Parameters::getNumberOfClients() ; i++)
		to_client[i]=-1;	
 	to_order=(int*)malloc(sizeof(int)*BFT::Parameters::getOrderCount() );
	for(int i=0 ; i<BFT::Parameters::getOrderCount() ; i++)
		to_order[i]=-1;
 	to_exec=(int*)malloc(sizeof(int)*BFT::Parameters::getExecutionCount() );
	for(int i=0 ; i<BFT::Parameters::getExecutionCount() ; i++)
		to_exec[i]=-1;
 	to_filter=(int*)malloc(sizeof(int)*BFT::Parameters::getFilterCount() );
	for(int i=0 ; i<BFT::Parameters::getFilterCount() ; i++)
		to_filter[i]=-1;
}

BaseNode::~BaseNode()
{
	free(to_client);
	free(to_order);
	free(to_exec);
	free(to_filter);
}

int BaseNode::sendToClient(boost::shared_ptr<char> _bytes, int _bytes_size, int _id)
{
	if ( BFT::util::CLIENT == myRole && _id == myid)
		return _bytes_size;

	if( (_id >= BFT::Parameters::getNumberOfClients()) || (_id < 0))
	{
		fprintf(stderr, "CLIENT.%d does not exist\n",BFT::Parameters::getNumberOfClients());
		exit(33);
	}
	
//	boost::unique_lock<boost::mutex>(send_mutex);
	
	if(to_client[_id] == -1)
	{
		//socket not open yet...
		to_client[_id] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

		struct sockaddr_in client_addr;
		
		client_addr.sin_family = AF_INET;
		client_addr.sin_port =  htons( membership.get()->getClientNode(_id).get()->getPort() );
		client_addr.sin_addr.s_addr = membership.get()->getClientNode(_id).get()->getAddr();
		
//		while ( connect(to_client[_id], (sockaddr*) &client_addr, sizeof(client_addr)) < 0)
//		{
//			fprintf(stderr, "unable to open a connection with client %d, errno:%d\n", _id, errno);
//			sleep(1);	
//		}
		
		if( connect(to_client[_id], (sockaddr*) &client_addr, sizeof(client_addr)) != 0)
		{
			fprintf(stderr, "unable to open a connection with client %d, errno:%d\n", _id, errno);
			to_client[_id] = -1;
			return 0;
			//sleep(1);	
		}

		
		
	}
	
	// socket already opened, send the message	
	
	char* msg_bytes = (char*)malloc(_bytes_size+8); 
	*(uint32_t*)msg_bytes = htonl(_bytes_size);
	memcpy((void*)(msg_bytes + 4), (const void*)_bytes.get(), _bytes_size);
	*(uint32_t*)(msg_bytes + _bytes_size + 4) = htonl(_bytes_size);
	
	int sentBytes;
	//	sentBytes=send( to_client[_id], msg_bytes, _bytes_size + 8 , MSG_NOSIGNAL);
	sentBytes=send( to_client[_id], msg_bytes, _bytes_size + 8, 0);
	if(sentBytes!=_bytes_size +8)
	{
		fprintf(stderr, "error in sendig message to exec %d, errno: %d\n", _id, errno);
		shutdown(to_client[_id], SHUT_RDWR);
		to_client[_id] = -1;
		//exit(42);
	}	
	free(msg_bytes);
	
	return sentBytes;
}

int BaseNode::sendToOrder(boost::shared_ptr<char> _bytes, int _bytes_size, int _id)
{
	if ( BFT::util::ORDER == myRole && _id == myid)
		return _bytes_size;
		
	if( (_id >= BFT::Parameters::getOrderCount()) || (_id < 0))
	{
		fprintf(stderr, "ORDER.%d does not exist\n",BFT::Parameters::getOrderCount());
		exit(37);
	}
	
//	boost::unique_lock<boost::mutex>(send_mutex);
	
	if(to_order[_id] == -1)
	{
		//socket not open yet...
		to_order[_id] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		
		struct sockaddr_in order_addr;
		
		order_addr.sin_family = AF_INET;
		order_addr.sin_port =  htons( membership.get()->getOrderNode(_id).get()->getPort() );
		order_addr.sin_addr.s_addr = membership.get()->getOrderNode(_id).get()->getAddr();
		
//		while ( connect(to_order[_id], (struct sockaddr *) & order_addr, sizeof(order_addr)) < 0)
//		{
//			fprintf(stderr, "unable to open a connection with order %d, errno:%d\n", _id, errno);
//			sleep(1);
//		}

		if ( connect(to_order[_id], (struct sockaddr *) & order_addr, sizeof(order_addr)) != 0)
		{
//			fprintf(stderr, "unable to open a connection with order %d, errno:%d\n", _id, errno);
			to_order[_id] = -1;
			return 0;
		}

	}
	
	// socket already opened, send the message	
	
	// socket already opened, send the message	
	
	char* msg_bytes = (char*)malloc(_bytes_size+8); 
	*(uint32_t*)msg_bytes = htonl(_bytes_size);
	memcpy((void*)(msg_bytes + 4), (const void*)_bytes.get(), _bytes_size);
	*(uint32_t*)(msg_bytes + _bytes_size + 4) = htonl(_bytes_size);
	
	int sentBytes;
	//	sentBytes=send( to_order[_id], msg_bytes, _bytes_size + 8 , MSG_NOSIGNAL);
	sentBytes=send( to_order[_id], msg_bytes, _bytes_size + 8 , 0);
	if(sentBytes!=_bytes_size +8)
	{
//		fprintf(stderr, "error in sendig message to order %d, errno: %d\n", _id, errno);
		shutdown(to_order[_id], SHUT_RDWR);
		to_order[_id] = -1;
		//exit(42);
	}	
	free(msg_bytes);

///////////////////////

//	char marker_bytes[MARKER_SIZE];
//	*(uint32_t*)marker_bytes = htonl(_bytes_size);
//	int sentBytes;
//	sentBytes=send( to_order[_id], marker_bytes, MARKER_SIZE,0);
//	if(sentBytes!=MARKER_SIZE)
//	{
//		fprintf(stderr, "error in sendig head marker to exec %d, errno: %d\n", _id, errno);
//		shutdown(to_order[_id], SHUT_RDWR);
//		to_order[_id] = -1;
//	}

//	sentBytes = send( to_order[_id], _bytes.get(), _bytes_size,0);
//	if(sentBytes!=_bytes_size)
//	{
//		fprintf(stderr, "error in sendig message to exec %d, errno: %d\n", _id, errno);
//		shutdown(to_order[_id], SHUT_RDWR);
//		to_order[_id] = -1;
//	}
//	sentBytes=send( to_order[_id], marker_bytes, MARKER_SIZE,0);
//	if(sentBytes!=MARKER_SIZE)
//	{
//		fprintf(stderr, "error in sendig tail marker to exec %d, errno: %d\n", _id, errno);
//		shutdown(to_order[_id], SHUT_RDWR);
//		to_order[_id] = -1;
//	}

////////////////////////////


	
	//return sentBytes;
	return _bytes_size;
}

int BaseNode::sendToExec(boost::shared_ptr<char> _bytes, int _bytes_size, int _id)
{
	if (BFT::util::EXEC == myRole && _id == myid)
		return _bytes_size;

	if( (_id >= BFT::Parameters::getExecutionCount()) || (_id < 0))
	{
		fprintf(stderr, "EXEC.%d does not exist\n",BFT::Parameters::getExecutionCount());
		exit(40);
	}

//	boost::unique_lock<boost::mutex>(send_mutex);
	
	if(to_exec[_id] == -1)
	{
		//socket not open yet...
		to_exec[_id] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		
		struct sockaddr_in exec_addr;
		
		exec_addr.sin_family = AF_INET;
		exec_addr.sin_port =  htons( membership.get()->getExecNode(_id).get()->getPort() );
		exec_addr.sin_addr.s_addr = membership.get()->getExecNode(_id).get()->getAddr();
		
//		while ( connect(to_exec[_id], (struct sockaddr *) & exec_addr, sizeof(exec_addr)) < 0)
//		{
//			fprintf(stderr, "unable to open a connection with exec %d, errno:%d\n", _id, errno);
//			sleep(1);
//		}

		if ( connect(to_exec[_id], (struct sockaddr *) & exec_addr, sizeof(exec_addr)) != 0)
		{
			fprintf(stderr, "unable to open a connection with exec %d, errno:%d\n", _id, errno);
			to_exec[_id] = -1;
			return 0;
		}
	}
	
	// socket already opened, send the message	
	
	char* msg_bytes = (char*)malloc(_bytes_size+8); 
	*(uint32_t*)msg_bytes = htonl(_bytes_size);
	memcpy((void*)(msg_bytes + 4), (const void*)_bytes.get(), _bytes_size);
	*(uint32_t*)(msg_bytes + _bytes_size + 4) = htonl(_bytes_size);
	
	int sentBytes;
	//	sentBytes=send( to_exec[_id], msg_bytes, _bytes_size + 8 , MSG_NOSIGNAL);
	sentBytes=send( to_exec[_id], msg_bytes, _bytes_size + 8 , 0);
	if(sentBytes!=_bytes_size +8)
	{
		fprintf(stderr, "error in sendig message to exec %d, errno: %d\n", _id, errno);
		shutdown(to_exec[_id], SHUT_RDWR);
		to_exec[_id] = -1;
		//exit(42);
	}	
	free(msg_bytes);
	
//	char marker_bytes[MARKER_SIZE];
//	*(uint32_t*)marker_bytes = htonl(_bytes_size);
//	int sentBytes;
//	sentBytes=send( to_exec[_id], marker_bytes, MARKER_SIZE, 0);
//	if(sentBytes!=MARKER_SIZE)
//	{
//		fprintf(stderr, "error in sendig head marker to exec %d, errno: %d\n", _id, errno);
//		shutdown(to_exec[_id], SHUT_RDWR);
//		to_exec[_id] = -1;
//		//exit(42);
//	}

//	sentBytes = send( to_exec[_id], _bytes.get(), _bytes_size, 0);
//	if(sentBytes!=_bytes_size)
//	{
//		fprintf(stderr, "error in sendig message to exec %d, errno: %d\n", _id, errno);
//		shutdown(to_exec[_id], SHUT_RDWR);
//		to_exec[_id] = -1;
//		//exit(42);
//	}
////	*(uint32_t*)marker_bytes = htonl(_bytes_size);
//	sentBytes=send( to_exec[_id], marker_bytes, MARKER_SIZE,0);
//	if(sentBytes!=MARKER_SIZE)
//	{
//		fprintf(stderr, "error in sendig tail marker to exec %d, errno: %d\n", _id, errno);
//		shutdown(to_exec[_id], SHUT_RDWR);
//		to_exec[_id] = -1;
//		//exit(42);
//	}

	
	return sentBytes;
}

int BaseNode::sendToFilter(boost::shared_ptr<char> _bytes, int _bytes_size, int _id)
{
	if ( BFT::util::FILTER == myRole && _id == myid)
		return _bytes_size;

	if( (_id >= BFT::Parameters::getFilterCount()) || (_id < 0))
	{
		fprintf(stderr, "FILTER.%d does not exist\n",BFT::Parameters::getFilterCount());
		exit(43);
	}

//	boost::unique_lock<boost::mutex>(send_mutex);
	
	if(to_filter[_id] == -1)
	{
		//socket not open yet...
		to_filter[_id] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		
		struct sockaddr_in filter_addr;
		
		filter_addr.sin_family = AF_INET;
		filter_addr.sin_port =  htons( membership.get()->getClientNode(_id).get()->getPort() );
		filter_addr.sin_addr.s_addr = membership.get()->getClientNode(_id).get()->getAddr();
		
		while ( connect(to_filter[_id],  (struct sockaddr *) & filter_addr, sizeof(filter_addr)) != 0)
		{
			fprintf(stderr, "unable to open a connection with filter %d, errno:%d\n", _id, errno);
			sleep(1);
		}
	}
	
	// socket already opened, send the message	
	
	char* msg_bytes = (char*)malloc(_bytes_size+8); 
	*(uint32_t*)msg_bytes = htonl(_bytes_size);
	memcpy((void*)(msg_bytes + 4), (const void*)_bytes.get(), _bytes_size);
	*(uint32_t*)(msg_bytes + _bytes_size + 4) = htonl(_bytes_size);
	
	int sentBytes;
	//	sentBytes=send( to_filter[_id], msg_bytes, _bytes_size + 8 , MSG_NOSIGNAL);
	sentBytes=send( to_filter[_id], msg_bytes, _bytes_size + 8 , 0);
	if(sentBytes!=_bytes_size +8)
	{
		fprintf(stderr, "error in sendig message to filter %d, errno: %d\n", _id, errno);
		shutdown(to_filter[_id], SHUT_RDWR);
		to_filter[_id] = -1;
		//exit(42);
	}	
	free(msg_bytes);
	
	return sentBytes;
}

// send a message to the principal _role.id
int BaseNode::sendToNode(boost::shared_ptr<char> _bytes, int _bytes_size, BFT::util::Role _role, int _id)
{
	switch(_role)
	{
		case BFT::util::CLIENT:
			return sendToClient(_bytes, _bytes_size,_id);
			break;
		case BFT::util::ORDER:
			return sendToOrder(_bytes, _bytes_size, _id);
			break;
		case BFT::util::EXEC:
			return sendToExec(_bytes, _bytes_size,_id);
			break;
		case BFT::util::FILTER:
			return sendToFilter( _bytes, _bytes_size, _id);
			break;
		default:
			fprintf(stderr, "trying to send a message to an unknown role\n");
			exit(32);
	}		
}

int BaseNode::sendToAllClients(boost::shared_ptr<char> _bytes, int _bytes_size)
{
	int minsent=-1;
	
	for (int i=0; i<BFT::Parameters::getNumberOfClients(); i++)
		minsent = std::min(minsent, sendToClient(_bytes, _bytes_size, i));

	return minsent;
}

int BaseNode::sendToAllOrder(boost::shared_ptr<char> _bytes, int _bytes_size)
{
	int minsent=-1;
	
	for (int i=0; i<BFT::Parameters::getOrderCount(); i++)
		minsent = std::min(minsent, sendToOrder(_bytes, _bytes_size, i));

	return minsent;
}

int BaseNode::sendToAllExec(boost::shared_ptr<char> _bytes, int _bytes_size)
{
	int minsent=-1;
	
	for (int i=0; i<BFT::Parameters::getExecutionCount(); i++)
		minsent = std::min(minsent, sendToExec(_bytes, _bytes_size, i));

	return minsent;
}

int BaseNode::sendToAllFilter(boost::shared_ptr<char> _bytes, int _bytes_size)
{
	int minsent=-1;
	
	for (int i=0; i<BFT::Parameters::getFilterCount(); i++)
		minsent = std::min(minsent, sendToFilter(_bytes, _bytes_size, i));
			
	return minsent;
}

int BaseNode::sendToAllRole(BFT::util::Role _role, boost::shared_ptr<char> _bytes, int _bytes_size)
{
	switch (_role)
	{
		case BFT::util::CLIENT:
			return sendToAllClients(_bytes, _bytes_size);
			break;
		case BFT::util::ORDER:
			return sendToAllOrder(_bytes, _bytes_size);
			break;
		case BFT::util::EXEC:
			return sendToAllExec(_bytes, _bytes_size);
			break;
		case BFT::util::FILTER:
			return sendToAllFilter(_bytes, _bytes_size);
			break;
		default:
			fprintf(stderr,"unable to send to all role %d, unknown _role %d\n",_role,_role);
			exit(46);
	}
	
}

int BaseNode::sendToAll(boost::shared_ptr<char> _bytes, int _bytes_size)
{
	int minsent;
	
	minsent = sendToAllClients(_bytes, _bytes_size);
	minsent = std::min(minsent, sendToAllOrder(_bytes, _bytes_size));
	minsent = std::min(minsent, sendToAllExec(_bytes, _bytes_size));
	minsent = std::min(minsent, sendToAllFilter(_bytes, _bytes_size));
	
	return minsent;
}

// TODO
void BaseNode::authenticateOrderMacArrayMessage(boost::shared_ptr<BFT::messages::MacArrayMessage> _mam)
{
	//TODO: do something here!!!
		
}

//TODO
bool BaseNode::validateOrderMacArrayMessage(boost::shared_ptr<BFT::messages::MacArrayMessage> _mam)
{
	//TODO: insert real validation code here
	return true;
}


// TODO
void BaseNode::authenticateExecMacArrayMessage(boost::shared_ptr<BFT::messages::MacArrayMessage> _mam)
{
	//TODO: do something here!!!
		
}

// TODO
void BaseNode::authenticateExecMacMessage(boost::shared_ptr<BFT::messages::MacMessage> _mam, int _index)
{
	//TODO: do something here!!!
		
}

// TODO
void BaseNode::authenticateOrderMacMessage(boost::shared_ptr<BFT::messages::MacMessage> _mam, int _index)
{
	//TODO: do something here!!!
		
}

bool BaseNode::validateExecMacArrayMessage(boost::shared_ptr<BFT::messages::MacArrayMessage> _mam)
{
	//TODO: insert real validation code here
	return true;
}

bool BaseNode::validateClientMacArrayMessage(boost::shared_ptr<BFT::messages::MacArrayMessage> _mam)
{
	//TODO: insert real validation code here
	return true;
}

bool BaseNode::validateExecMacMessage(boost::shared_ptr<BFT::messages::MacMessage> _mam)
{
	//TODO: insert real validation code here
	return true;
}


bool BaseNode::validateOrderMacMessage(boost::shared_ptr<BFT::messages::MacMessage> _mam)
{
	//TODO: insert real validation code here
	return true;
}

int BaseNode::getMyClientIndex()
{
	if (membership.get()->getMyRole() == BFT::util::CLIENT)
		return myid;
	else
		return -1;
}

int BaseNode::getMyOrderIndex()
{
	if (membership.get()->getMyRole() == BFT::util::ORDER)
		return myid;
	else
		return -1;
}

int BaseNode::getMyExecutionIndex()
{
	if (membership.get()->getMyRole() == BFT::util::EXEC)
		return myid;
	else
		return -1;
}
int BaseNode::getMyFilterIndex()
{
	if (membership.get()->getMyRole() == BFT::util::FILTER)
		return myid;
	else
		return -1;
}


